//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner
{
    #region Using directives
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using System.Windows;
    using System.Windows.Controls;
    using Microsoft.Research.ScientificWorkflow.Validators;
    using System.Collections.ObjectModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using System.Resources;
    using System.Windows.Input;
    #endregion 

    /// <summary>
    /// Abstract class for all the UI elements.
    /// </summary>
    public abstract class TridentUIElement : TridentBaseUIControl, ITridentUIElement, IBindingEndpoint
    {
        #region Constructor

        protected TridentUIElement(BaseModel model)
        {
            this.Model = model;
            this.Loaded += new RoutedEventHandler(this.OnTridentUIElementLoaded);
            this.MouseDown += new MouseButtonEventHandler(this.OnMouseDown);
            
            if (!string.IsNullOrEmpty(model.UniqueId))
            {
                this.Name = model.UniqueId + "Element";
            }
        }

        #endregion Constructor

        #region Properties

        /// <summary>
        ///  Gets and sets the connection element to the left.
        /// </summary>
        public FrameworkElement LeftControlFlowConnectionElement
        {
            get;
            set;
        }

        /// <summary>
        ///  Gets and sets the connection element to the right.
        /// </summary>
        public FrameworkElement RightControlFlowConnectionElement
        {
            get;
            set;
        } 
        
        /// <summary>
        /// Gets and sets the activity model associated with the control.
        /// </summary>
        public BaseModel Model
        {
            get;
            set;
        }

        /// <summary>
        /// Gets and sets the main holding border.
        /// </summary>
        public Border MainBorder
        {
            get;
            set;
        }

        /// <summary>
        /// Gets and sets the composer control.
        /// </summary>
        public ComposerControl ComposerCtrl
        {
            get;
            set;
        }

        #endregion

        #region Abstract events

        /// <summary>
        /// Event signifying that the activity has to be deleted.
        /// </summary>
        public abstract event EventHandler DeleteElement;

        /// <summary>
        /// Event signifying that the activity has to be selected.
        /// </summary>
        public abstract event EventHandler SelectElement;

        public abstract event EventHandler ShowProperties;

        public abstract event EventHandler InsertIntoSubsection;

        public abstract event EventHandler<ProvenanceModeEventArgs> ProvenanceMenuClick;

        /// <summary>
        /// Occurs when update is clicked.
        /// </summary>
        public abstract event EventHandler<ModelEventArgs> UpdateClick;

        /// <summary>
        /// Display the parameter outline.
        /// </summary>
        public abstract void ShowParameterOutline(bool showInputOutline);

        /// <summary>
        /// Hide the parameter outline.
        /// </summary>
        public abstract void HideParameterOutline(bool hideInputOutline);
        #endregion

        #region Abstract methods

        /// <summary>
        /// Apply seleted style to the workflow activity element.
        /// </summary>
        public abstract void ApplySelectedStyle();

        /// <summary>
        /// Apply Unseleted stlye to the workflow activity element.
        /// </summary>
        public abstract void ApplyUnselectedStyle();

        /// <summary>
        /// 
        /// </summary>
        public abstract void CreateContextMenu();

        /// <summary>
        /// 
        /// </summary>
        public abstract void CreateProvenanceMenu();
        
        #region IBindingEndpoint Members

        /// <summary>
        ///  Get the binding startpoint.
        /// </summary>
        /// <param name="relativeTo"></param>
        /// <returns></returns>
        public abstract Point GetBindingStartPoint(UIElement relativeTo);

        /// <summary>
        /// Get the binding endpoint.
        /// </summary>
        /// <param name="relativeTo"></param>
        /// <returns></returns>
        public abstract Point GetBindingEndpoint(UIElement relativeTo);

        #endregion

        #endregion

        #region Concrete methods

        /// <summary>
        /// Get the current position of the activity model in the parent.
        /// </summary>
        /// <returns>Position of the activity model.</returns>
        public virtual int FetchUIElementPosition()
        {
            int index = -1;
            if (this.Model.ParentModel != null && this.ComposerCtrl != null)
            {
                ICompositeUIElement parentElem = this.ComposerCtrl.UIElements[this.Model.ParentModel.UniqueId] as ICompositeUIElement;
                if (parentElem != null && parentElem.ChildElements.Contains(this))
                {
                    index = parentElem.ChildElements.IndexOf(this);
                }
            }

            return index;
        }

        /// <summary>
        /// Get the composer control instance.
        /// </summary>
        internal void GetComposerControl()
        {
            string composerCtrlName = (string)this.GetValue(ComposerControl.ComposerControlNameProperty);
            this.ComposerCtrl = Application.Current.MainWindow.FindName(composerCtrlName) as ComposerControl;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnTridentUIElementLoaded(object sender, RoutedEventArgs e)
        {
            this.GetComposerControl();
        }

        /// <summary>
        /// Fired on MouseDown of the element.
        /// </summary>
        /// <param name="sender">
        /// Trident element.
        /// </param>
        /// <param name="e">
        /// MouseButtonEventArgs.
        /// </param>
        private void OnMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (this.ComposerCtrl != null)
            {
                ITridentUIElement selectedElement = sender as ITridentUIElement;
                if (e.ChangedButton == MouseButton.Left)                    
                {
                    this.OnSelectElement(selectedElement);
                }
                else if (e.ChangedButton == MouseButton.Right && !this.ComposerCtrl.CurrentSelectedElements.Contains(selectedElement))
                {
                    this.ComposerCtrl.CurrentSelectedElements.RemoveAllItems();
                    this.OnSelectElement(selectedElement);
                }
            }
        }

        private void OnSelectElement(ITridentUIElement selectedElement)
        {
            if (!(Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)))
            {
                this.ComposerCtrl.CurrentSelectedElements.RemoveAllItems();
            }

            if (null != selectedElement)
            {
                this.ComposerCtrl.CurrentSelectedElements.SelectChildrenUpTo(selectedElement);
                Keyboard.Focus(selectedElement as UIElement);
                this.Focus();
            }
        }

        #endregion
    }
}
